/******************************************************************************\
    Socket.hpp contains a class of socket.
    Copyright (C) 2008  Victor Vinogradov (fly3333@gmail.com)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
\******************************************************************************/

#ifndef __SOCKET_HPP
#define __SOCKET_HPP

#include <winsock2.h>
#include "Object.hpp"

class cSubSocket : public cSubObject
{
	SOCKET Socket;
public:
	cSubSocket( SOCKET socket )
		: Socket( socket )
	{
	}
	~cSubSocket()
	{
		closesocket( Socket );
	}
	SOCKET Handle() const
	{
		return Socket;
	}
};

class cSocket : public cObject< cSubSocket >
{
public:
	bool Create( int af, int type, int protocol )
	{
		SOCKET handle = socket( af, type, protocol );
		if( handle == INVALID_SOCKET )
			return false;
		cSubSocket *sub_socket;
		try
		{
			sub_socket = new cSubSocket( handle );
		}
		catch( ... )
		{
			return false;
		}
		SwitchToSubObject( sub_socket );
		return true;
	}
	bool Accept( const cSocket &socket, struct sockaddr *addr, int *addrlen )
	{
		SOCKET handle = accept( socket, addr, addrlen );
		if( handle == INVALID_SOCKET )
			return false;
		cSubSocket *sub_socket;
		try
		{
			sub_socket = new cSubSocket( handle );
		}
		catch( ... )
		{
			return false;
		}
		SwitchToSubObject( sub_socket );
		return true;
	}
	void Close()
	{
		SwitchToSubObject( NULL );
	}
	operator SOCKET() const 
	{
		if( GetSubObject() == NULL )
			return INVALID_SOCKET;
		return GetSubObject()->Handle();
	}
	int Bind( const struct sockaddr *name, int namelen )
	{
		if( GetSubObject() == NULL )
			return SOCKET_ERROR;
		return bind( GetSubObject()->Handle(), name, namelen );
	}
	int Connect( const struct sockaddr *name, int namelen )
	{
		if( GetSubObject() == NULL )
			return SOCKET_ERROR;
		return connect( GetSubObject()->Handle(), name, namelen );
	}
	int Listen( int backlog )
	{
		if( GetSubObject() == NULL )
			return SOCKET_ERROR;
		return listen( GetSubObject()->Handle(), backlog );
	}
	int Receive( char *buf, int len, int flags )
	{
		if( GetSubObject() == NULL )
			return SOCKET_ERROR;
		return recv( GetSubObject()->Handle(), buf, len, flags );
	}
	int Send( const char *buf, int len, int flags )
	{
		if( GetSubObject() == NULL )
			return SOCKET_ERROR;
		return send( GetSubObject()->Handle(), buf, len, flags );
	}
	int SendTo( const char *buf, int len, int flags, const struct sockaddr *to, int tolen )
	{
		if( GetSubObject() == NULL )
			return SOCKET_ERROR;
		return sendto( GetSubObject()->Handle(), buf, len, flags, to, tolen );
	}
	int ReceiveFrom( char *buf, int len, int flags, struct sockaddr *from, int *fromlen )
	{
		if( GetSubObject() == NULL )
			return SOCKET_ERROR;
		return recvfrom( GetSubObject()->Handle(), buf, len, flags, from, fromlen );
	}
	int GetSockName( struct sockaddr *name, int *namelen )
	{
		if( GetSubObject() == NULL )
			return SOCKET_ERROR;
		return ::getsockname( GetSubObject()->Handle(), name, namelen );
	}
	int GetPeerName( struct sockaddr *name, int *namelen )
	{
		if( GetSubObject() == NULL )
			return SOCKET_ERROR;
		return ::getpeername( GetSubObject()->Handle(), name, namelen );
	}
	int IOControl( long command, u_long *parameter )
	{
		if( GetSubObject() == NULL )
			return SOCKET_ERROR;
		return ioctlsocket( GetSubObject()->Handle(), command, parameter );
	}
};

struct sInAddr : public in_addr
{
	sInAddr( u_char b1, u_char b2, u_char b3, u_char b4 )
	{
		S_un.S_un_b.s_b1 = b1;
		S_un.S_un_b.s_b2 = b2;
		S_un.S_un_b.s_b3 = b3;
		S_un.S_un_b.s_b4 = b4;
	}
	sInAddr( u_short w1, u_short w2 )
	{
		S_un.S_un_w.s_w1 = w1;
		S_un.S_un_w.s_w2 = w2;
	}
	sInAddr( u_long addr )
	{
		S_un.S_addr = addr;
	}
};

struct sSockAddrIn : public sockaddr_in
{
	sSockAddrIn()
	{
		memset( this, 0, sizeof( sockaddr_in ) );
	}
	sSockAddrIn( short family, u_short port, const in_addr &address )
	{
		sin_family = family;
		sin_port = port;
		sin_addr = address;
		memset( sin_zero, 0, sizeof( sin_zero ) );
	}
};

#endif
